# -*- coding: utf-8 -*-
import re
import codecs
import locale

locale.setlocale(locale.LC_ALL, "fi_FI.UTF-8")

# aakkostusta varten
_transtab = { u"š" : u"s", u"ž" : u"z", u"w" : u"v", u"å" : u"{", u"ä" : u"|", u"ö" : u"}" }
transtab = dict((ord(k), _transtab[k]) for k in _transtab)


# VÄLIAIKAINEN
gmuutokset = []

# lyhenteet
class Kielet(): 
    def __init__(self):
        self.fi = u"Suomi"
        self.en = u"Englanti"
        self.sv = u"Ruotsi"
        self.no = u"Norja"
        self.es = u"Espanja"
        self.fr = u"Ranska"
        self.de = u"Saksa"
        self.it = u"Italia"
class Sanalk():
    def __init__(self):
        self.adj = u"Adjektiivi"
        self.adv = u"Adverbi"
        self.sub = u"Substantiivi"
        self.ver = u"Verbi"
        self.eri = u"Erisnimi"
        self.int = u"Interjektio"

k = Kielet()
sl = Sanalk()

class Osio(object):
    @staticmethod
    def tee_tekstista(teksti):
        """
        Tekee osion annetusta tekstistä. Tekstin täytyy sisältää ensimmäisenä otsikko.

        @p teksti (unicode)  parsittava teksti, jossa on ensimmäisenä otsikko, eikä muita saman 
                             tai ylemmän tason otsikoita
        @return (Osio)/None  tekstin juuriosio
        """
        m = re.match("\\s*(=+)(.*?)(=+)\\s*\n", teksti)
        if m:
            return Osio(m.group(2), teksti[m.end()-1:])
        return None

    def __init__(self, nimi, teksti = None, vanhempi = None):
        self._tekstiosa = None
        self._osiot = []
        self.vanhempi = vanhempi
        self.nimi = nimi
        self.taso = 1
        if vanhempi:
            self.taso = vanhempi.taso + 1

        if teksti:
            try:
                self.indeksoi(teksti)
            except DuplikaattiException as e:
                raise e

    def copy(self, osio):
        """
        Kopioi osion tiedot tähän Osioon.
        
        @p osio (Osio) 
        @return (Osio)
        """
        self.osio = osio
        self._osiot = osio._osiot
        self._tekstiosa = osio._tekstiosa
        self.vanhempi = osio.vanhempi
        self.nimi = osio.nimi
        self.taso = osio.taso
        return self


    def replace(self, osio, uusi):
        """
        Korvaa lapsiosion `osio` osiolla `uusi`.
        """
        i = self._osiot.index(osio)
        if i == -1:
            raise EiTamanLapsiException(self, osio)
        self._osiot[i] = uusi

#    def __str__(self):
#        return u"<Osio: " + self.nimi + u"(" + str(self.taso) + ")>"

    def __unicode__(self):
        return u"<Osio: " + self.nimi + u"(" + str(self.taso) + ")>"
    
    def aliosiot(self, nimi = None):
        """
        Palauttaa aliosiot, joiden otsikko on `nimi` tai kaikki osiot, jos `nimi` = None.
        """
        loydot = []
        for a in self._osiot:
            if nimi == None or a.nimi == nimi:
                loydot.append(a)
        return loydot

    def jalkelaiset_f(self, func, param = None, _loydot = []):
        """
        Palauttaa kaikki jälkeläisosiot, joille funktio `func` palauttaa True.

        @p func    funktio, joka ottaa yhden (Osio) tai kaksi parametria ja palauttaa totuusarvon
        @p param   funktiolle välitettävä parametri
        @p _loydot [Osio]  (sisäiseen käyttöön)
        """
        for a in self._osiot:
            if func == None or func(a, param):
                _loydot.append(a)
            a.jalkelaiset_f(func, param, _loydot)
        return _loydot

    def jalkelaiset(self, nimi):
        """
        Palauttaa kaikki jälkeläisosiot, joilla on nimi `nimi` tai kaikki jos nimeä ei ole annettu.

        @p nimi (unicode) 
        """
        return self.jalkelaiset_f(lambda o, p: nimi == None or o.nimi == nimi)
        
    def tason_osiot(self, taso, _loydot = []):
        """
        Palauttaa kaikki tason `taso` osiot, jotka ovat tämän osion jälkeläisiä.

        @p taso    (int)
        @p _loydot [Osio]  (sisäiseen käyttöön)
        """
        for o in self._osiot:
            if o.taso == taso:
               _loydot.append(o)
            else:
               _loydot =  o.tason_osiot(taso, _loydot)
        return _loydot
                         
    def indeksoi(self, oteksti):
        """
        Indeksoi osion tekstin `oteksti`. Teksti voi sisältää aliosiot, mutta ei `self`in tason otsikkoa.

        @p oteksti (unicode)
        """
        # lisätään rivinvaihto ennen ensimmäistä otsikkoa helpottamaan parsimista
        if oteksti[0] == u"=":    oteksti = u"\n" + oteksti

        re_m = re.compile(u"\n(=+)\\s*(.*?)\\s*(=+)\\s*\n")

        # asetetaan taso ensimmäisen aliotsikon mukaan (muuten taso on 1 tai vanhemmalta peritty)
        if self.taso == 1: 
            m = re.search(re_m, oteksti)
            if m: self.taso = len(m.group(1))-1

#        print u"self({}).taso: {}".format(self.nimi, self.taso)

        osat = []
        m = True
        a = 0
        while m:
            m = re.search(re_m, oteksti[a:])
            if m:
                otstaso = len(m.group(1))  # =-merkkien määrä
                otsikko = m.group(2)
#                print u"ostikkko: {}, taso: {}".format(otsikko, otstaso)
                osat.append((a + m.start()+1, otstaso, otsikko))
                osat.append((a + m.end(), 0, None)) # tekstiosan alku
                a = a + m.end() - 1
        osat.append((len(oteksti), 0, None))

        # osion oma tekstiosa
        to = oteksti[0:osat[0][0]].strip()
        if to != u"": self.set_tekstiosa(to)

        nyko = self
        nykt = self.taso
        for i in xrange(0, len(osat)-2, 2):
            alku = osat[i+1][0] # tekstiosan alku
            lopp = osat[i+2][0] # -"-        loppu
            taso = osat[i][1]
            otso = osat[i][2]
            text = oteksti[alku:lopp].strip()
            uusi = Osio(otso, text)
#            print "taso: {0}, nykt: {1}".format(taso, nykt)
            # jos otsikon taso on liian syvällä tai taso menee tämän osion alle: # 
            if taso > nykt + 1 or taso <= self.taso: 
                raise OtsikonTasoException(u"taso: {0}, vanhemman taso: {1}".format(taso, nykt))
            elif taso <= nykt:
                while taso <= nykt:
                    nyko = nyko.vanhempi
                    nykt = nykt - 1
            uusi.set_parent(nyko)
            nyko._osiot.append(uusi)
            nyko = uusi
            nykt = nykt + 1

        return True
                          

    def set_tekstiosa(self, to):
        if to:
            to = to.strip()
#        print u"setting to[{}]".format(to)
        self._tekstiosa = to

    def tekstiosa(self):
        return self._tekstiosa


    def teksti(self):
        to = self.tekstiosa()
        a = []
        if self.taso == 1:
            pass
        else:
            a.append(u"\n" + (u"=" * self.taso) + self.nimi + (u"=" * self.taso))

        if to != None:
            if self.taso > 1: a.append(u"\n")
            a.append(to)
            a.append(u"\n")

        for o in self._osiot:
            a.append(o.teksti())

        return u"".join(a)

    def set_teksti(self, teksti):
        self._osiot = []
        try:
            self.indeksoi(teksti)
        except DuplikaattiException as e:
            raise e

    def indeksi(self):
        """
        Palauttaa Osion indeksin vanhemman lapsilistassa.
        """
        pl = self.vanhempi._osiot
        for i in xrange(0, len(pl)):
            if pl[i] == self:
                return i
        return -1


    def set_parent(self, vanhempi):
        """
        Asettaa `self`-osiolle uuden vanhemman ja päivittää kaikki lapsiosiot.
        """
        self.vanhempi = vanhempi
        self.taso = vanhempi.taso + 1
        for o in self._osiot:
            o.set_parent(self)

    def lisaa_aliosio(self, i, osio):
        """
        Lisää `osion` kohtaan `i`.
        """
        l = len(self._osiot)
        # jos negatiivinen i, lasketaan lopusta
        if i < 0: i = i + l + 1
        if i >= 0 and i <= l:
#            print u"lisätään kohtaan {0}".format(i)
            self._osiot.insert(i, osio)
            osio.set_parent(self)
            return osio
        return None

    def lisaa_ennen_f(self, uusi, func):
        """
        Lisää osion `osio` ennen kohtaa, jossa funktio `func` palauttaa > 0 tai viimeiseksi.
        @p osio (Osio)
        @p func (function((Osio), (Osio) uusi))
        """
        for o in self._osiot:
            c = func(o, uusi)
            if c  > 0:
                o.lisaa_eteen(uusi)
                return uusi
        # lisätään viimeiseksi
        self.lisaa_aliosio(-1, uusi)
        return uusi

    def lisaa_eteen(self, osio):
        """
        Lisää `osion` tämän osion eteen.
        """
        if self.vanhempi:
            return self.vanhempi.lisaa_aliosio(self.indeksi(), osio)
        return None

    def lisaa_jalkeen(self, osio):
        """
        Lisää `osion` tämän osion perään.
        """
        if self.vanhempi:
            return self.vanhempi.lisaa_aliosio(self.indeksi() + 1, osio)
        return None

    def eka_lapsi(self):
        if len(self._osiot) > 0:
            return self._osiot[0]
        return None

    def vika_lapsi(self):
        if len(self._osiot) > 0:
            return self._osiot[-1]
        return None

    def irrota(self):
        """
        Poistaa osion vanhemmastaan ja palauttaa sen.
        """
##        print u"self.indexi: {0}".format(self.indeksi())
        del self.vanhempi._osiot[self.indeksi()]
        self.vanhempi = None
        return self

class ParseriException(Exception):
    def __init__(self, teksti):
        self.teksti = teksti

    def __str__(self):
        return self.teksti

class OsiotaEiOleException(ParseriException):
    pass

class OtsikonTasoException(ParseriException):
    """
    Otsikon taso suhteessa vanhemman tasoon on mahdoton.
    """
    def __str__(self):
        return u"virheellinen otsikon taso:" + self.teksti


class DuplikaattiException(ParseriException):
    """
    Saman niminen aliosio on jo osiossa, joka ei voi sisältää kuin yhden tietyn nimisen osion.
    """
    def __init__(self, duplnimi):
        self.duplnimi = duplnimi

    def __str__(self):
        return u"tuplaotsikko: {}".format(self.duplnimi)

class LiianMontaOsiotaException(Exception):
    def __init__(self, osio, ali):
        self.osio = osio
        self.ali = ali

    def __str__(self):
        return repr(self.osio, self.ali)

class EiTamanLapsiException(Exception):
    def __init__(self, osio, ali):
        self.osio = osio
        self.ali = ali

    def __str__(self):
        return u"Osiolla {} ei ole lapsiosiota {}.".format(self.osio, self.ali)
    
class WtOsio(Osio):
    def __init__(self, osio):
        self.copy(osio)
        self.vanhempi.replace(osio, self)
        self.muutokset = {u'yhdistetty' : False, u'järjestetty' : False }

        try:
            self.jarjesta_osiot()
        except DuplikaattiException as e:
            raise e



    def jarjesta_osiot(self, key=None):
        self._osiot.sort(key=key)
        # yritetään yhdistaa samanlaiset osiot
        si = 0
        i = 0
        ei = 0
        while True:
            l = len(self._osiot)
#            print "l: {}, i: {}, si: {}".format(l, i, si)
            for i in xrange(si, l-1):
#                print "kiekka"
                a = self._osiot[i]
                b = self._osiot[i+1]
                if a.nimi == b.nimi:
                    print u"*********************** a: " + a.nimi + ", b: " + b.nimi + " *************"
                    # Jos toinen ei ole tyhjä, ei yhdistetä.
                    if a.nimi != u"Liittyvät sanat" and not (a.tekstiosa() == None or a.tekstiosa() == u"" \
                                or b.tekstiosa() == None or b.tekstiosa() == u""):
                        raise DuplikaattiException(a.nimi)
                    else:
                        print u"Yhdistetään kaksi {}-osiota".format(a.nimi)
                        if not (a.tekstiosa() == b.tekstiosa):
                            to = a.tekstiosa() or u""
                            if to != u"": to += u"\n"
                            to += b.tekstiosa() or u""
                            a.set_tekstiosa(to)
                        a._osiot += b._osiot
                        self.muutokset[u'yhdistetty'] = True
                        gmuutokset.append(u"Yhdistetty kaksi " + a.nimi + u" -osiota")
                        self._osiot.remove(b)
                        si = i
                        break
                ei = i
#                print "l: {}, ei: {}, si: {}".format(l, ei, si)
            if ei >= l-2:
                break


class WtLiittyvat(WtOsio):
    otsikot = {
        u"Rinnakkaiset kirjoitusasut" : 0, 
        u"Vaihtoehtoiset kirjoitusasut" : 1,  # ei 'virallinen'
        u"Rinnakkaismuodot" : 10, 
        u"Vaihtoehtoiset muodot" : 11,        # ei 'virallinen'
        u"Synonyymit" : 20, 
        u"Vastakohdat" : 30, 
        u"Johdokset" : 40, 
        u"Yhdyssanat" : 50, 
        u"Sanaliitot" : 55, 
        u"Yhdyssanat ja sanaliitot" : 60, 
        u"Tästä johtuvat tytärkielten sanat" : 70, 
        u"Lainat muissa kielissä" : 80, 
        u"Yläkäsitteet" : 90, 
        u"Vieruskäsitteet" : 100, 
        u"Rinnakkaiskäsitteet" : 111,  # ei virallinen
        u"Alakäsitteet" : 110, 
        u"Osakäsitteet" : 120, 
        u"Kokonaiskäsitteet" : 130, 
        }

    def __init__(self, osio):
        try:
            super(WtLiittyvat, self).__init__(osio)
        except DuplikaattiException as e:
            raise e

    def jarjesta_osiot(self):
       super(WtLiittyvat, self).jarjesta_osiot(key=WtLiittyvat.make_key)

       for c in self._osiot:
           tmp = WtOsio(c) # korvautuu

    def slaliosio(self, nimi):
        """
        Palauttaa liittyvät sanat -aliotsikon `nimi`.
        @p nimi (string)  osion nimi
        @p return (WtOsio)  aliosio
        """
        l = self.aliosiot(nimi)
        if len(l) > 1:
            raise LiianMontaOsiotaException(self, nimi)
        elif len(l) == 0:
            return None
        return l[0]

    @staticmethod
    def hae_paikka(title):
        if title in WtLiittyvat.otsikot:
            return WtLiittyvat.otsikot[title]
        return -1

    @staticmethod
    def make_key(o):
        # Tuntemattomat otsikot ensimmäisiksi
        if not o.nimi in WtSanalk.otsikot:
#            print u"Tuntematon otsikko: " + o.nimi
            return -1

        return WtLiittyvat.otsikot[o.nimi]

    def lisaa_slaliosio(self, uusi):
        """
        Lisää uuden Liittyvät sanat -osion.
        @p uusi (WtOsio)  uusi osio
        """
        def comp(a, b):
            if a.nimi == b.nimi: return 0
            return cmp(WtLiittyvat.otsikot[a.nimi], WtLiittyvat.otsikot[b.nimi])

        if self.slaliosio(uusi.nimi) != None:
            print u"Virhe: Liittyvät sanat aliosio '{1}' on jo!".format(None, uusi.nimi)
            return None

        return self.lisaa_ennen_f(uusi, comp)


class WtAliosio(WtOsio):
    def __init__(self, osio):
        super(WtAliosio, self).__init__(osio)

class WtSanalk(WtOsio):
    otsikot = {
        u"Ääntäminen" : 0, 
        u"Taivutus" : 1, 
        u"Huomautukset" : 2, 
        u"Etymologia" : 3, 
        u"Käännökset" : 4, 
        u"Lainaukset" : 5, 
        u"Liittyvät sanat" : 6, 
        u"Idiomit" : 7, 
        u"Aiheesta muualla" : 8, 
        u"Lähteet" : 9, 
        }

    def __init__(self, osio):
        try:
            super(WtSanalk, self).__init__(osio)
        except DuplikaattiException as e:
            raise e

    def slaliosio(self, nimi):
        """
        Palauttaa sanaluokan aliotsikon `nimi`.
        @p nimi (string)  osion nimi
        """
        l = self.aliosiot(nimi)
        if len(l) > 1:
            print u"osiot: {}".format(l)
            raise LiianMontaOsiotaException(self, nimi)
        elif len(l) == 0:
            return None
        if nimi == u"Liittyvät sanat":
            return WtLiittyvat(l[0])
        return l[0]

    def slaliosio2(self, nimi):
        """
        Palauttaa sanaluokan aliotsikon tai liittyvät sanat -aliotsikon `nimi`.
        @p nimi (string)  osion nimi
        """
        os = self.slaliosio(nimi)
        if os:
            return os

        os = self.slaliosio(u"Liittyvät sanat")
        if os:
            return os.slaliosio(nimi)
        return None


    @staticmethod
    def hae_paikka(title):
        if title in WtSanalk.otsikot:
            return WtSanalk.otsikot[title]
        return -1

    @staticmethod
    def make_key(o):
        # Tuntemattomat otsikot ensimmäisiksi
        if not o.nimi in WtSanalk.otsikot:
#            print u"Tuntematon otsikko: " + o.nimi
            return -1

        return WtSanalk.otsikot[o.nimi]

    def jarjesta_osiot(self):
       super(WtSanalk, self).jarjesta_osiot(key=WtSanalk.make_key)

       for c in self._osiot:
           if c.nimi == u"Liittyvät sanat":
               tmp = WtLiittyvat(c) # korvautuu
           else:
               tmp = WtOsio(c)

    def lisaa_slaliosio(self, uusi):
        """
        Lisää uuden aliosion sanaluokalle.
        @p uusi (WtOsio)  uusi osio
        """

        if self.aliosiot(uusi.nimi) != []:
            raise DuplikaattiException(uusi.nimi)
        
        if uusi.nimi == u"Liittyvät sanat":
            liit = self.slaliosio(u"Liittyvät sanat")
            if liit != None:
                print u"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Liittyvät on jo"
                return liit
            else:
                return self.lisaa_ennen_f(uusi, lambda a, b: cmp(WtSanalk.make_key(a), WtSanalk.make_key(b)))
        else:
            return self.lisaa_ennen_f(uusi, lambda a, b: cmp(WtSanalk.make_key(a), WtSanalk.make_key(b)))

    def lisaa_oikeaan_paikkaan(self, osio):
        """
        Lisää osion `osio`, joko tämän osion tai liittyvät sanat -osion alle, riippuen siitä
        kumpaan se kuuluu. Jos osio ei kuulu kumpaankaan ei lisää osiota ja palauttaa None.
        Muuten palauttaa `osion`.
        """
        if WtSanalk.hae_paikka(osio.nimi) > -1:
            return self.lisaa_slaliosio(osio)
        else:
            if WtLiittyvat.hae_paikka(osio.nimi) > -1:
                liits = self.aliosiot(u"Liittyvät sanat")
                if liits != []:
                    liit = liits[0]
                else:
                    liit = WtLiittyvat(self.lisaa_slaliosio(Osio(u"Liittyvät sanat")))
                return liit.lisaa_slaliosio(osio)
        return None


class WtKieli(WtOsio):
    def __init__(self, osio):
        try:
            super(WtKieli, self).__init__(osio)
        except DuplikaattiException as e:
            raise e

    def sanaluokat(self, otsikko):
        """
        Palauttaa sanaluokan `otsikko` tai None.
        
        @p otsikko unicode 
        @return    [WtSanalk] sanaluokkaosiotaulukko
        """

        return map(lambda o: WtSanalk(o), self.osio.aliosiot(otsikko))

    @staticmethod
    def make_key(o):
        """
        """
        if o.nimi == u"Lähteet": return (+1, None)
        return (0, o.nimi.translate(transtab).lower())

    @staticmethod
    def comp(a, b):
        if a[0] == b[0]:
            return cmp(a[1], a[1])
        return cmp(a[0], b[0])

    def jarjesta_osiot(self):
#        print u"Järjestetään kieli"

        # Sanaluokka-osioita – järjestetään vain, ei yritetä yhdistää saman nimisiä.
        self._osiot.sort(key=WtKieli.make_key)

        # Muutetaan osiot WtSanalk-osioiksi.
        for c in self._osiot:
           tmp = WtSanalk(c) # korvautuu

    def lisaa_sanaluokka(self, uusi, jos_on=True):
        """
        Lisää uuden sanaluokkaosion.
        @p uusi   (WtOsio)   lisättävä sanaluokkaosio
        @p jos_on (bool)   jos True, palautetaan None, jos sanaluokka on jo
        @return   (Osio, None)  `uusi`, jos sanaluokka lisättiin, None jos ei
        """

        if jos_on:
            if self.aliosiot(uusi.nimi) != []:
                raise OsioOnJoException(uusi.nimi)
        return WtSanalk(self.lisaa_ennen_f(uusi, lambda a, b: WtKieli.comp(WtKieli.make_key(a), WtKieli.make_key(b))))
        

class WiktSivu(object):
    def __init__(self, teksti):
        # VÄLIAIKAINEN
        self.muutokset = []
        self.text, self.klinkit = WiktSivu.erota_kielilinkit(teksti)
        self.juuri = Osio(u"[juuri]", self.text)
        try:
            self.jarjesta_osiot()
        except DuplikaattiException as e:
            raise e

        

    @staticmethod
    def erota_kielilinkit(teksti):
        """
        Palauttaa 2-tuplen, jossa ensimmäinen alkio sisältää muun tekstin paitsi kieli- ja luokkalinkkejä
        ja toinen kielilinkit ja luokkalinkit.
        """

        if teksti[-1] != u"\n":
            teksti += u"\n"

        klt = u""
        # Etsitään kielilinkit artikkelin lopusta
        re_m = u"\n(\[\[([-a-z]+):[^:|]+\]\][ \n\t]*)+$"
        m = re.search(re_m, teksti)
        if m:
            klt = m.group()[1:]
            klt = klt.replace(r"\]\][ \t\n]*", u"\]\]")

        # poistetaan kielilinkit artikkelista
        oteksti = re.sub(re_m, "", teksti)


        # Etsitään kaikki luokkalinkit artikkelista
        re_m = u"\n(\[\[Luokka:.+\]\][ \t]*)+"
        luokat = []
        m = True
        a = 0
        while m:
            m = re.search(re_m, teksti[a:])
            if m:
                print u"Luokka: " + m.group()[1:],
                luokat.append(m.group()[1:])
                a = a + m.end()

        # poistetaan luokat artikkelista
        oteksti = re.sub(re_m, "", oteksti)

        luokat_txt =  u"]]\n[[".join((u"".join(luokat)).split(r"]][["))        
        kielilinkit_txt = re.sub(r"\n+", "\n", klt.strip())

        print(u"Luokat: \n" + luokat_txt)
        print(u"Kielilinkit: \n" + kielilinkit_txt)


        return [oteksti, luokat_txt + u"\n\n" + kielilinkit_txt + "\n"]

#        m = re.search(u"(\[\[([-a-z]+|Luokka):.*\]\][ \n\t]*)*$", teksti)
#        if m:
#            return teksti[:m.start()], teksti[m.start():]
            

    def kieliosio(self, kieli):
        """
        Paluttaa kieliosion. Jos kieliosiota ei ole tai niitä on enemmän kuin yksi, palauttaa None.
        @p kieli (string)    kielen nimi
        @return (Osio, None)
        """
        osiot = self.juuri.aliosiot(kieli)
        if len(osiot) == 1:
            return WtKieli(osiot[0])
        return None
        
    @staticmethod
    def make_key(o):
        """
        Palauttaa -1, jos kieli `a` tulee ennen kieltä `b`. +1 jos päinvastoin. 0, jos kielet ovat samat.
        """
        if o.nimi == u"Kansainvälinen": return (-2, None)
        if o.nimi == u"Suomi": return (-1, None)

        if o.nimi == u"Lähteet": return (+1, None)
        if o.nimi == u"Viitteet": return (+2, None)

        return (0, o.nimi.translate(transtab).lower())

    def lisaa_kieli(self, uusi):
        """
        Lisää uuden kieliosion `uusi` (WtOsio) oikeaan kohtaan.
        """

        if self.juuri.aliosiot(uusi.nimi) != []:
            print u"Virhe: kieli {0} on jo.".format(uusi.nimi)
            return False

        return WtKieli(self.juuri.lisaa_ennen_f(uusi, lambda a, b: cmp(WiktSivu.make_key(a), WiktSivu.make_key(b))))
    
    def jarjesta_osiot(self):
#        print u"Järjestetään juuri"

        # VÄLIAIKAINEN
        gmuutokset = []

        self.juuri._osiot.sort(key=WiktSivu.make_key)
        for c in self.juuri._osiot:
            tmp = WtKieli(c) # korvautuu

        # Tarkistetaan onko samannimisiä osioita
        for i in xrange(0, len(self.juuri._osiot) - 1):
            a = self.juuri._osiot[i]
            b = self.juuri._osiot[i+1]
            if a.nimi == b.nimi:
                raise DuplikaattiException(a.nimi)

        self.muutokset = gmuutokset

    def headeri(self):
        """
        Palauttaa tekstin ennen sivun ensimmäistä otsikkoa.
        """
        return self.juuri.tekstiosa()

    def set_headeri(self, teksti):
        """
        Asettaa tekstin ennen sivun ensimmäistä otsikkoa.

        @p teksti unicode
        """
        self.juuri.set_tekstiosa(teksti)

    def teksti(self):
        """
        Palauttaa koko sivun tekstin, mukaan lukien kielilinkit.
        """
        return self.juuri.teksti() + u"\n" + self.klinkit


    def get_muutokset(self):
        """
        Palauttaa kaikilta lapsielementeiltä niille tehdyt muutokset.
        """
        return self.juuri.get_muutokset()


if __name__ == u"__main__":
    import sys
    fn = sys.argv[1]
    with codecs.open(fn, u"r", encoding="utf-8") as f:
        lines = f.read()
        
        
        
    w = WiktSivu(lines)
    # da = w.lisaa_kieli(Osio("Tanska", u"Hello from Denmark!\n"))
    
    # w.lisaa_sanaluokka("Tanska", Osio("Substantiivi", u"Hello from substantiivi!"))
    # w.lisaa_sanaluokka("Tanska", Osio("Adjektiivi", u"Hello from adjektiivi!"))
    # print w.lisaa_sanaluokka(k.fi, Osio(sl.sub, u"Terve"), False)

    # w.set_headeri("{{katso|naku}}")
#    # print w.headeri()
    # print u"-----"
#    #print w.sl_aliosio(w.sanaluokat(k.fi, sl.sub)[0], u"Käännökset")

#    kint = w.kieliosio(u"")
#    aak = kint.sanaluokat(u"Aakkonen")
#    print subs[1].teksti()

##    print subs[1].slaliosio(u"Liittyvät sanat").teksti() #.slaliosio("Synonyymit").teksti()


#    print subs[1].slaliosio(u"Liittyvät sanat").slaliosio("Synonyymit").teksti()

#    slsub = lsubs = w.sanaluokat(u"Suomi", u"Substantiivi")[0]
 #   w.lisaa_sl_aliosio(slsub, Osio(u"Lähteet", u"* [http://kaino.kotus.fi/sanat/nykysuomi/ Kotimaisten kielten keskuksen nykysuomen sanalista] v. 1. (taivutustyyppi = 3)"))
#    print w.teksti()


#    aak[0].set_tekstiosa("hello")
#   print u"to: {}".format(aak[0].tekstiosa())


    print w.teksti()
